/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd;

import java.util.ArrayList;
import java.util.List;

import org.zhiqim.httpd.bio.HttpBioConnection;
import org.zhiqim.httpd.nio.HttpNioConnection;
import org.zhiqim.kernel.config.Group;
import org.zhiqim.kernel.control.Queue;
import org.zhiqim.kernel.control.ThreaderController;
import org.zhiqim.kernel.control.Threadx;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.schedule.Interval;
import org.zhiqim.kernel.schedule.Task;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Strings;

/**
 * HTTP监听器接口定义
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public abstract class HttpListener extends Threadx implements Task, HttpdConstants
{
    protected static final Log log = LogFactory.getLog(HttpListener.class);
    
    private final HttpServer server;                        //服务器
    private final List<HttpConnection> connList;            //连接列表
    
    protected Group listenerGroup;                          //监听参数
    protected String listenerName;                          //监听名称
    protected int listenerPort;                             //监听端口
    protected String listenerIp;                            //监听网卡IP地址
    
    protected int soTimeoutMs;                              //套接字等待连接时长，单位毫秒，等待60*1000秒
    protected int soLingerS;                                //套接字关闭连接时长，(TIME_WAIT和操作系统有关)，单位秒，最大65535，即默认2MSL=240秒，实际应用中常用30秒
    protected int soRecvBuf;                                //套接字接收缓冲区大小，默认KiB
    protected int soSendBuf;                                //套接字接收缓冲区大小，默认128KiB
    protected int soIdleTimeMs;                             //套接字长连接最大空闲时长，单位毫秒，默认53*1000毫秒
    
    protected int poMinSize;                                //线程池最小值
    protected int poMaxSize;                                //线程池最大值
    protected int poIdleTime;                               //线程池线程空闲时长
    protected int poQueueSize;                              //线程池队列大小
    
    protected ThreaderController controller;                //线程池控制器
    protected Log logger;                                   //日志记录器
    
    private Interval interval;                              //定时器，检查超时连接
    
    public HttpListener(HttpServer server)
    {
        this.server = server;
        this.connList = new ArrayList<>();
    }
    
    public void setGroup(Group group)
    {
        this.listenerGroup = group;
    }
    
    @Override /** 线程名 */
    protected String getThreadName()
    {
        return listenerName;
    }
    
    @Override /** 线程开启前 */
    protected boolean openBefore()
    {
        listenerName = listenerGroup.getId();
        listenerIp = listenerGroup.getString(_LISTEN_IP_ADDRESS_);
        listenerPort = listenerGroup.getInt(_LISTEN_PORT_);
        Asserts.as((listenerPort >= 1 && listenerPort <= 65535)?null:"未配置[%s]的port配置项，或配置的值不是端口值[1-65535]");

        soTimeoutMs = listenerGroup.getInt(_LISTEN_SO_ITMEOUT_, 60) * 1000;
        soLingerS = listenerGroup.getInt(_LISTEN_SO_LINGER_, 20);
        soRecvBuf = listenerGroup.getInt(_LISTEN_SO_RECV_BUF_, KiB);
        soSendBuf = listenerGroup.getInt(_LISTEN_SO_SEND_BUF_, 128 * KiB);
        soIdleTimeMs = listenerGroup.getInt(_LISTEN_SO_IDLE_TIME_, 53) * 1000;
        
        poMinSize = listenerGroup.getInt(_LISTEN_PO_MIN_SIZE_, 20);
        poMaxSize = listenerGroup.getInt(_LISTEN_PO_MAX_SIZE_, 200);
        poIdleTime = listenerGroup.getInt(_LISTEN_PO_IDLE_TIME_, 600);
        poQueueSize = listenerGroup.getInt(_LISTEN_PO_QUEUE_SIZE_, poMaxSize * 3);
        
        if (listenerGroup.isTrue(_LISTEN_ACCESS_LOG_))
        {//创建访问日志
            logger = LogFactory.getLog(listenerName);
        }
        
        if (soIdleTimeMs > 0)
        {//配置的Socket时长则作定时检查
            interval = Interval.shedule(this, soIdleTimeMs);
        }
        
        //初始化线程控制器
        controller = new ThreaderController();
        controller.set(poMinSize, poMaxSize, poIdleTime, poQueueSize);
        controller.setThreadName(listenerName);
        controller.create();
        return true;
    }
    
    @Override /** 线程关闭后 */
    protected void closeAfter()
    {
        //1.关闭定时任务
        if (interval != null)
        {
            interval.close();
            interval = null;
        }
        
        //2.关闭处理控制器
        if (controller != null)
        {
            controller.close();
            controller = null;
        }
        
        //3.清理线程池
        connList.clear();
        
        //4.打印退出日志
        log.info(toString() + "退出");
    }
    
    /**************************************************************************/
    //通用实现方法
    /**************************************************************************/
    
    /** 服务 */
    public HttpServer getServer()
    {
        return server;
    }
    
    /** 协议 */
    public String getScheme()
    {
        return _HTTP_;
    }
    
    /** 监听端口 */
    public int getPort()
    {
        return listenerPort;
    }
    
    /** 监听名 */
    public String getName()
    {
        return listenerName;
    }
    
    /** 监听网卡地址 */
    public String getIpAddress()
    {
        return listenerIp;
    }
    
    /**************************************************************************/
    //Socket参数
    /**************************************************************************/
    
    /** Socket连接超时时长 */
    public int getSoTimeoutMs()
    {
        return soTimeoutMs;
    }
    
    /** Socket接收缓冲大小 */
    public int getSoRecvBuf()
    {
        return soRecvBuf;
    }
    
    /** Socket发送缓冲大小 */
    public int getSoSendBuf()
    {
        return soSendBuf;
    }
    
    /** Socket长连接空闲时长 */
    public int getSoIdleTimeMs()
    {
        return soIdleTimeMs;
    }
    
    /**************************************************************************/
    //线程池参数，主要用于监控，请谨慎调用
    /**************************************************************************/
    
    /** 线程池允许的最大值 */
    public int getPoMaxSize()
    {
        return poMaxSize;
    }
    
    /** 线程池定义的最小值 */
    public int getPoMinSize()
    {
        return poMinSize;
    }
    
    /** 线程池允许的最大队列数 */
    public int getPoQueueSize()
    {
        return poQueueSize;
    }
    
    /** 线程池线程允许的空闲时长 */
    public int getPoIdleTime()
    {
        return poIdleTime;
    }
    
    /** 线程池控制器 */
    public ThreaderController getController()
    {
        return controller;
    }
    
    /** 线程池中当前活动数 */
    public int getThreaderActiveSize()
    {
        return controller.getThreaderActiveSize();
    }
    
    /** 线程池当前大小 */
    public int getThreaderSize()
    {
        return controller.getThreaderSize();
    }
    
    /** 线程池当前队列 */
    public Queue<Runnable> getQueue()
    {
        return controller.getQueue();
    }
    
    /** 线程池当前队列数 */
    public int getQueueSize()
    {
        return controller.size();
    }
    
    /**************************************************************************/
    //监听下的连接完成
    /**************************************************************************/
    
    /** 删除连接 */
    public void remove(HttpConnection conn)
    {
        synchronized (connList)
        {
            connList.remove(conn);
        }
    }
    
    /** 增加连接 */
    protected HttpNioConnection add(HttpNioConnection conn)
    {
        synchronized (connList)
        {
            connList.add(conn);
        }
        return conn;
    }
    
    /** 删除连接 */
    protected HttpBioConnection add(HttpBioConnection conn)
    {
        synchronized (connList)
        {
            connList.add(conn);
        }
        return conn;
    }
    
    /** 线程池处理完记录日志 */
    public void finished(HttpHeader header)
    {
        if (logger == null)
            return;
        
        //打印日志
        HttpSender sender = header.getSender();
        
        String remoteAddr = header.getRemoteAddr();
        String headerLine = header.getHeaderLine();
        int listenerPort = header.getListenerPort();
        int status = sender.getStatus();
        
        long length = 0;
        if (sender instanceof HttpSenderImpl)
            length = ((HttpSenderImpl)sender).getFlushLength();
        else if (sender instanceof HttpResponseImpl)
            length = ((HttpResponseImpl)sender).getFlushLength();
        
        String message = new StringBuilder()
               .append("[").append(remoteAddr).append("]")
               .append(" [").append(status).append("]")
               .append(" [").append(listenerPort).append("]")
               .append(" [").append(length).append("]")
               .append(" [").append(headerLine).append("]")
               .toString();
        
        logger.info(message);
    }
    
    /**************************************************************************/
    //连接检查
    /**************************************************************************/

    @Override /** 定义检查 */
    public void execute()
    {
        int wsNum = 0, activeNum = 0;
        List<HttpConnection> list = new ArrayList<>();
        synchronized (connList)
        {
            for (HttpConnection conn : connList)
                list.add(conn);
        }
        
        for (HttpConnection conn : list)
        {
            if (conn.isWebSocket())
            {
                wsNum++;
                continue;
            }
            
            if (conn.isActive())
            {
                activeNum++;
                continue;
            }
            
            if (conn.isOvertime())
            {
                log.debug("连接[%s]空闲过长，正常关闭", conn.getId());
                conn.close();
            }
        }
        
        int totalNum = list.size(), idleNum = totalNum - wsNum - activeNum;
        list.clear();list = null;
        log.info("允许最大连接[%s]，当前总连接[%s]=WS[%s]+活动[%s]+空闲[%s]，允许空闲[%s]秒", poMaxSize, totalNum, wsNum, activeNum, idleNum, soIdleTimeMs/1000);
    }
    
    @Override
    public String toString()
    {
        return new StringBuilder(Strings.toUpperCase(getScheme())).append("监听:[")
            .append("name:").append(listenerName).append(";")
            .append("port:").append(listenerPort).append("]")
            .toString();
    }
}
